﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;

namespace Mimoza.Transport
{
    /// <summary>
    /// Generic class for TransferObject.
    /// Realize special method of base class for specefied type T.
    /// ReplyT - type of reply TransferObject if T is request
    /// To process request client should override method ReplyT ProcessRequest()
    /// Provide special static method to register TransferObject in transport - RegisterInTransport()
    /// </summary>
    public class TransferObjectGeneric<T, ReplyT> : TransferObject where T : class, new()
    {
        /// <summary>
        /// Delegate for processing incoming requests
        /// </summary>
        public delegate ReplyT Handler(T obj);
        
        static public void RegisterInTransport()
        {
            Transport.Dispatcher.RegisterTransferObject((new T()) as TransferObject);
        }

        static public void RegisterHandler(Handler handler)
        {
            RegisterInTransport();
            m_handler += handler;            
        }

        static public void DeleteHandler(Handler handler)
        {
            m_handler -= handler;            
        }

        void Process(TransferObject request, out TransferObject reply)
        {
            reply = m_handler(request as T) as TransferObject;
        }

        override public void Serialize(Stream stream)
        {
            XmlSerializer ser = new XmlSerializer(typeof(T));
            ser.Serialize(stream, this);
        }

        override public TransferObject Deserialize(Stream stream)
        {
            XmlSerializer ser = new XmlSerializer(typeof(T));
            object obj = ser.Deserialize(stream);
            return (obj as TransferObject);
        }

        override public string TypeName
        {
            get
            {
                return typeof(T).ToString();
            }
        }

        override public string RemoteAddress 
        {
            get
            {
                return m_remoteAddr;
            }
            set
            {
                m_remoteAddr = value;
            }
        }


        override public void ProcessRequest(Connection conn, out TransferObject replyData)
        {
            Connection = conn;
            replyData = (ProcessRequest() as TransferObject);
            
            TransferObject handlerReplyData = null;
            if (m_handler != null)
            {
                handlerReplyData = m_handler(this as T) as TransferObject;
            }

            if (replyData == null && handlerReplyData != null)
            {
                replyData = handlerReplyData;
            }
        }

        [System.Xml.Serialization.XmlIgnore]
        override public Connection Connection 
        {
            get { return m_connection; }
            set { m_connection = value; }
        }

        /// <summary>
        /// This method can be overrride in client class to process request into object
        /// </summary>
        virtual public ReplyT ProcessRequest()
        {
            return default(ReplyT);
        }

        string m_remoteAddr;
        Connection m_connection;

        static Handler m_handler;
    }

    /// <summary>
    /// Generic class for TransferObject.
    /// Reperesent request TransferObject without reply. This can be used in UDP.
    /// </summary>
    public class TransferObjectGenericWithoutReply<T> : TransferObjectGeneric<T, TransferObject> where T : class, new()
    {
    }

    /// <summary>
    /// Generic class for TransferObject.
    /// Reperesent request TransferObject that can produced diffrent type of replies. 
    /// </summary>
    public class TransferObjectGenericAnyReply<T> : TransferObjectGeneric<T, TransferObject> where T : class, new()
    {
    }

    /// <summary>
    /// Generic class for TransferObject.
    /// Reperesent reply TransferObject. 
    /// </summary>
    public class TransferObjectGenericReply<T> : TransferObjectGeneric<T, TransferObject> where T : class, new()
    {
    }


    /// <summary>
    /// Generic class for Handler. 
    /// </summary>
    public class TransferObjectGenericHandler<T, ReplyT> where T : class, new()
    {
        /// <summary>
        /// Delegate for processing incoming requests
        /// </summary>
        public delegate ReplyT Handler(T obj);

        public void RegisterHandler(Handler handler)
        {
            m_handler += handler;
        }

        void Process(TransferObject request, out TransferObject reply)
        {
            reply = m_handler(request as T) as TransferObject;
        }

        Handler m_handler;
    }
}
